package com.example.javapoet.demo.intermediate;

import com.squareup.javapoet.*;

import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * JavaPoet中级示例 - 生成Lambda表达式和方法引用
 */
public class LambdaAndMethodReferenceGeneration {

    public static void main(String[] args) {
        // 生成包含Lambda表达式的代码
        generateCodeWithLambdaExpressions();
        
        // 生成包含方法引用的代码
        generateCodeWithMethodReferences();
        
        // 生成包含复杂Lambda表达式的代码
        generateCodeWithComplexLambdas();
    }

    /**
     * 生成包含Lambda表达式的代码
     */
    public static void generateCodeWithLambdaExpressions() {
        // 创建方法
        MethodSpec filterMethod = MethodSpec.methodBuilder("filterStrings")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "strings")
                .addStatement("return strings.stream()\n" +
                        "        .filter(s -> s.length() > 3)\n" +
                        "        .filter(s -> s.startsWith(\"J\"))\n" +
                        "        .collect($T.toList())",
                        Collectors.class)
                .build();
        
        MethodSpec mapMethod = MethodSpec.methodBuilder("transformStrings")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "strings")
                .addStatement("return strings.stream()\n" +
                        "        .map(s -> s.toUpperCase())\n" +
                        "        .map(s -> \"Prefix_\" + s)\n" +
                        "        .collect($T.toList())",
                        Collectors.class)
                .build();
        
        MethodSpec reduceMethod = MethodSpec.methodBuilder("concatenateStrings")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(String.class)
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "strings")
                .addStatement("return strings.stream()\n" +
                        "        .reduce(\"\", (acc, s) -> acc + \", \" + s)\n" +
                        "        .replaceFirst(\"^, \", \"\")")
                .build();
        
        MethodSpec customLambdaMethod = MethodSpec.methodBuilder("processWithCustomLogic")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(Integer.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "input")
                .addStatement("$T<$T<$T>> lengthFilter = s -> s.length() > 5", 
                        Predicate.class, String.class, String.class)
                .addStatement("$T<$T, $T> lengthMapper = s -> s.length()", 
                        Function.class, String.class, Integer.class)
                .addStatement("return input.stream()\n" +
                        "        .filter(lengthFilter)\n" +
                        "        .map(lengthMapper)\n" +
                        "        .collect($T.toList())",
                        Collectors.class)
                .build();
        
        // 创建示例方法
        MethodSpec exampleMethod = MethodSpec.methodBuilder("example")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addStatement("$T<$T> names = $T.asList(\"Java\", \"JavaScript\", \"Python\", \"C++\", \"Kotlin\")",
                        List.class, String.class, Arrays.class)
                .addStatement("$T<$T> filteredNames = filterStrings(names)", 
                        List.class, String.class)
                .addStatement("$T.out.println(\"Filtered names: \" + filteredNames)", System.class)
                .addStatement("$T<$T> transformedNames = transformStrings(names)", 
                        List.class, String.class)
                .addStatement("$T.out.println(\"Transformed names: \" + transformedNames)", System.class)
                .addStatement("$T concatenated = concatenateStrings(names)", String.class)
                .addStatement("$T.out.println(\"Concatenated names: \" + concatenated)", System.class)
                .addStatement("$T<$T> lengths = processWithCustomLogic(names)", 
                        List.class, Integer.class)
                .addStatement("$T.out.println(\"Lengths of long names: \" + lengths)", System.class)
                .build();
        
        // 创建类
        TypeSpec lambdaClass = TypeSpec.classBuilder("LambdaExamples")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(filterMethod)
                .addMethod(mapMethod)
                .addMethod(reduceMethod)
                .addMethod(customLambdaMethod)
                .addMethod(exampleMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.lambda", lambdaClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("生成LambdaExamples类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成LambdaExamples.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成包含方法引用的代码
     */
    public static void generateCodeWithMethodReferences() {
        // 创建方法
        MethodSpec staticMethodRefMethod = MethodSpec.methodBuilder("parseIntegers")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(Integer.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "strings")
                .addStatement("return strings.stream()\n" +
                        "        .map($T::parseInt)\n" +
                        "        .collect($T.toList())",
                        Integer.class, Collectors.class)
                .build();
        
        MethodSpec instanceMethodRefMethod = MethodSpec.methodBuilder("convertToUpperCase")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "strings")
                .addStatement("return strings.stream()\n" +
                        "        .map($T::toUpperCase)\n" +
                        "        .collect($T.toList())",
                        String.class, Collectors.class)
                .build();
        
        MethodSpec constructorRefMethod = MethodSpec.methodBuilder("createPersonObjects")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get("com.example.lambda", "Person")))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "names")
                .addStatement("return names.stream()\n" +
                        "        .map($T::new)\n" +
                        "        .collect($T.toList())",
                        ClassName.get("com.example.lambda", "Person"),
                        Collectors.class)
                .build();
        
        MethodSpec arbitraryInstanceMethodRefMethod = MethodSpec.methodBuilder("getNameLengths")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(Integer.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "names")
                .addStatement("return names.stream()\n" +
                        "        .map($T::length)\n" +
                        "        .collect($T.toList())",
                        String.class, Collectors.class)
                .build();
        
        // 创建示例方法
        MethodSpec exampleMethod = MethodSpec.methodBuilder("example")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addStatement("$T<$T> numberStrings = $T.asList(\"1\", \"2\", \"3\", \"4\", \"5\")",
                        List.class, String.class, Arrays.class)
                .addStatement("$T<$T> numbers = parseIntegers(numberStrings)", 
                        List.class, Integer.class)
                .addStatement("$T.out.println(\"Parsed integers: \" + numbers)", System.class)
                .addStatement("$T<$T> names = $T.asList(\"alice\", \"bob\", \"charlie\", \"david\")",
                        List.class, String.class, Arrays.class)
                .addStatement("$T<$T> upperCaseNames = convertToUpperCase(names)", 
                        List.class, String.class)
                .addStatement("$T.out.println(\"Uppercase names: \" + upperCaseNames)", System.class)
                .addStatement("$T<$T> nameLengths = getNameLengths(names)", 
                        List.class, Integer.class)
                .addStatement("$T.out.println(\"Name lengths: \" + nameLengths)", System.class)
                .addStatement("// Note: Person class would need to be defined for createPersonObjects to work")
                .build();
        
        // 创建Person类
        TypeSpec personClass = TypeSpec.classBuilder("Person")
                .addModifiers(Modifier.PUBLIC)
                .addField(String.class, "name", Modifier.PRIVATE)
                .addMethod(MethodSpec.constructorBuilder()
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(String.class, "name")
                        .addStatement("this.name = name")
                        .build())
                .addMethod(MethodSpec.methodBuilder("getName")
                        .addModifiers(Modifier.PUBLIC)
                        .returns(String.class)
                        .addStatement("return name")
                        .build())
                .addMethod(MethodSpec.methodBuilder("toString")
                        .addAnnotation(Override.class)
                        .addModifiers(Modifier.PUBLIC)
                        .returns(String.class)
                        .addStatement("return \"Person{name='\" + name + \"'}\"")
                        .build())
                .build();
        
        // 创建类
        TypeSpec methodRefClass = TypeSpec.classBuilder("MethodReferenceExamples")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(staticMethodRefMethod)
                .addMethod(instanceMethodRefMethod)
                .addMethod(constructorRefMethod)
                .addMethod(arbitraryInstanceMethodRefMethod)
                .addMethod(exampleMethod)
                .build();
        
        // 创建Java文件
        JavaFile personFile = JavaFile.builder("com.example.lambda", personClass)
                .build();
        
        JavaFile methodRefFile = JavaFile.builder("com.example.lambda", methodRefClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成Person类:");
            personFile.writeTo(System.out);
            
            System.out.println("\n生成MethodReferenceExamples类:");
            methodRefFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            personFile.writeTo(new File(path.toUri()));
            methodRefFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成Person.java和MethodReferenceExamples.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成包含复杂Lambda表达式的代码
     */
    public static void generateCodeWithComplexLambdas() {
        // 创建方法
        MethodSpec nestedLambdaMethod = MethodSpec.methodBuilder("createProcessor")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        ClassName.get(String.class),
                        ClassName.get(String.class)))
                .addParameter(String.class, "prefix")
                .addParameter(String.class, "suffix")
                .addStatement("return input -> {\n" +
                        "    String processed = input.trim();\n" +
                        "    if (processed.isEmpty()) {\n" +
                        "        return \"\";\n" +
                        "    }\n" +
                        "    return prefix + processed + suffix;\n" +
                        "}")
                .build();
        
        MethodSpec composingLambdasMethod = MethodSpec.methodBuilder("composeTransformations")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariable(TypeVariableName.get("T"))
                .addTypeVariable(TypeVariableName.get("R"))
                .addTypeVariable(TypeVariableName.get("V"))
                .returns(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        TypeVariableName.get("T"),
                        TypeVariableName.get("V")))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        TypeVariableName.get("T"),
                        TypeVariableName.get("R")), "f1")
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        TypeVariableName.get("R"),
                        TypeVariableName.get("V")), "f2")
                .addStatement("return value -> f2.apply(f1.apply(value))")
                .build();
        
        MethodSpec recursiveLambdaMethod = MethodSpec.methodBuilder("createFactorialCalculator")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        ClassName.get(Integer.class),
                        ClassName.get(Integer.class)))
                .addStatement("$T<$T, $T> factorial = new $T<>()", 
                        Function.class, Integer.class, Integer.class, Function.class)
                .addStatement("factorial = n -> n <= 1 ? 1 : n * factorial.apply(n - 1)")
                .addStatement("return factorial")
                .build();
        
        // 创建示例方法
        MethodSpec exampleMethod = MethodSpec.methodBuilder("example")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addStatement("$T<$T, $T> processor = createProcessor(\"<< \", \" >>\")",
                        Function.class, String.class, String.class)
                .addStatement("$T result1 = processor.apply(\"Hello World\")", String.class)
                .addStatement("$T.out.println(\"Processed text: \" + result1)", System.class)
                .addStatement("$T<$T, $T> toUpperCase = $T::toUpperCase", 
                        Function.class, String.class, String.class, String.class)
                .addStatement("$T<$T, $T> addExclamation = s -> s + \"!\"", 
                        Function.class, String.class, String.class)
                .addStatement("$T<$T, $T> composed = composeTransformations(toUpperCase, addExclamation)", 
                        Function.class, String.class, String.class)
                .addStatement("$T result2 = composed.apply(\"hello\")", String.class)
                .addStatement("$T.out.println(\"Composed result: \" + result2)", System.class)
                .addStatement("$T<$T, $T> factorialFn = createFactorialCalculator()", 
                        Function.class, Integer.class, Integer.class)
                .addStatement("$T.out.println(\"Factorial of 5: \" + factorialFn.apply(5))", System.class)
                .build();
        
        // 创建类
        TypeSpec complexLambdaClass = TypeSpec.classBuilder("ComplexLambdaExamples")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(nestedLambdaMethod)
                .addMethod(composingLambdasMethod)
                .addMethod(recursiveLambdaMethod)
                .addMethod(exampleMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.lambda", complexLambdaClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成ComplexLambdaExamples类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成ComplexLambdaExamples.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}